﻿
using System;
using System.Globalization;

using EnvDTE;

using EnvDTE80;

namespace VsExt.SolutionCodeAnalysis
{
    public class CodeAnalysis
    {
        private DTE2 _dte;

        public event EventHandler<TraceEventArgs> WriteToLog;

        ///-------------------------------------------------------------------------------------------------
        /// <summary>
        ///  Invoker for the WriteToTraceLog event.
        /// </summary>
        ///-------------------------------------------------------------------------------------------------
        private void onWriteToLog(string traceData)
        {
            TraceEventArgs e = new TraceEventArgs { Info = traceData };

            EventHandler<TraceEventArgs> handler = WriteToLog;
            if (handler != null)
            {
                handler(this, e);
            }
        }

        ///-------------------------------------------------------------------------------------------------
        /// <summary>
        ///  Public constructor.
        /// </summary>
        ///-------------------------------------------------------------------------------------------------
        public CodeAnalysis(DTE2 dte)
        {
            _dte = dte;
        }

        ///-------------------------------------------------------------------------------------------------
        /// <summary>
        ///  Enables code analysis for all valid projects in the solution.
        /// </summary>
        ///-------------------------------------------------------------------------------------------------
        public void Enable()
        {
            if (_dte == null || !_dte.Solution.IsOpen)
            {
                onWriteToLog("Solution is not open.");
                return;
            }

            enableOrDisableCodeAnalysisOnAllProjects(true);
        }

        ///-------------------------------------------------------------------------------------------------
        /// <summary>
        ///  Disables code analysis for all valid projects in the solution.
        /// </summary>
        ///-------------------------------------------------------------------------------------------------
        public void Disable()
        {
            if (_dte == null || !_dte.Solution.IsOpen)
            {
                onWriteToLog("Solution is not open.");
                return;
            }

            enableOrDisableCodeAnalysisOnAllProjects(false);
        }

        ///-------------------------------------------------------------------------------------------------
        /// <summary>
        ///  Indicates if code analysis is available on the specified project.
        /// </summary>
        ///-------------------------------------------------------------------------------------------------
        private static bool isValidProjectType(Project project)
        {
            bool isValidType;

            switch (project.Kind)
            {
                case ProjectGuids.vsDatabase:
                    isValidType = false;
                    break;
                case ProjectGuids.vsDatabaseOther:
                    isValidType = false;
                    break;
                case ProjectGuids.vsDeploymentCab:
                    isValidType = false;
                    break;
                case ProjectGuids.vsDeploymentMergeModule:
                    isValidType = false;
                    break;
                case ProjectGuids.vsDeploymentSetup:
                    isValidType = false;
                    break;
                case ProjectGuids.vsDeploymentSmartDeviceCab:
                    isValidType = false;
                    break;
                case ProjectGuids.vsDistributedSystem:
                    isValidType = false;
                    break;
                case ProjectGuids.vsLegacy2003SmartDeviceCSharp:
                    isValidType = false;
                    break;
                case ProjectGuids.vsLegacy2003SmartDeviceVBNET:
                    isValidType = false;
                    break;
                case ProjectGuids.vsSharePointWorkflow:
                    isValidType = false;
                    break;
                case ProjectGuids.vsTest:
                    isValidType = false;
                    break;
                case ProjectGuids.vsVisualDatabaseTools:
                    isValidType = false;
                    break;
                case ProjectGuids.vsWorkflowCSharp:
                    isValidType = false;
                    break;
                case ProjectGuids.vsWorkflowVBNET:
                    isValidType = false;
                    break;
                case ProjectGuids.vsVSTA:
                    isValidType = true;
                    break;
                case ProjectGuids.vsSmartDeviceCSharp:
                    isValidType = true;
                    break;
                case ProjectGuids.vsSmartDeviceVBNET:
                    isValidType = true;
                    break;
                case ProjectGuids.vsVSTO:
                    isValidType = true;
                    break;
                case ProjectGuids.vsWCF:
                    isValidType = true;
                    break;
                case ProjectGuids.vsWebApplication:
                    isValidType = true;
                    break;
                case ProjectGuids.vsWebSite:
                    isValidType = true;
                    break;
                case ProjectGuids.vsWindowsCSharp:
                    isValidType = true;
                    break;
                case ProjectGuids.vsWindowsVBNET:
                    isValidType = true;
                    break;
                case ProjectGuids.vsWindowsVisualCPP:
                    isValidType = true;
                    break;
                case ProjectGuids.vsWPF:
                    isValidType = true;
                    break;
                default:
                    isValidType = false;
                    break;
            }
            return isValidType;
        }


        ///-------------------------------------------------------------------------------------------------
        /// <summary>
        ///  Sets the RunCodeAnalysis project property for for the specified project.
        /// </summary>
        ///-------------------------------------------------------------------------------------------------
        private void changeCodeAnalysis(Project project, bool isCodeAnalysisEnabled)
        {
            if (project.Kind == Constants.vsProjectKindSolutionItems || project.Kind == Constants.vsProjectKindMisc)
            {
                foreach (ProjectItem projectItem in project.ProjectItems)
                {
                    if (projectItem.SubProject != null)
                    {
                        changeCodeAnalysis(projectItem.SubProject, isCodeAnalysisEnabled);
                    }
                }
            }
            else
            {
                try
                {
                    if (isValidProjectType(project))
                    {
                        setCodeAnalysis(project, isCodeAnalysisEnabled);
                    }
                    else
                    {
                        onWriteToLog(string.Format(CultureInfo.CurrentCulture, "Skipping project: {0} ({1})", project.Name, project.Kind));
                    }
                }
                catch (Exception exception)
                {
                    onWriteToLog(exception.Message);
                }
            }
        }

        ///-------------------------------------------------------------------------------------------------
        /// <summary>
        ///  Sets's the projects ConfigurationManager.ActiveConfiguration 'RunCodeAnalysis' property to the target value.
        /// </summary>
        ///-------------------------------------------------------------------------------------------------
        private void setCodeAnalysis(Project project, bool targetCodeAnalysisValue)
        {
            bool currentCodeAnalysisValue = (bool)project.ConfigurationManager.ActiveConfiguration.Properties.Item("RunCodeAnalysis").Value;

            if (currentCodeAnalysisValue != targetCodeAnalysisValue)
            {
                onWriteToLog("Changing project: " + project.Name + " from " + currentCodeAnalysisValue + " to " + targetCodeAnalysisValue + ".");
                project.ConfigurationManager.ActiveConfiguration.Properties.Item("RunCodeAnalysis").Value = targetCodeAnalysisValue;
            }
            else
            {
                onWriteToLog("Skipping project: " + project.Name + ", already at the correct Code Analysis value.");
            }
        }

        ///-------------------------------------------------------------------------------------------------
        /// <summary>
        /// Set's the RunCodeAnalysis configuration property to the value specified to each valid projects in the solution.
        /// </summary>
        ///-------------------------------------------------------------------------------------------------
        private void enableOrDisableCodeAnalysisOnAllProjects(bool enabled)
        {
            try
            {
                bool isCodeAnalysisEnabled = enabled;

                foreach (Project project in _dte.Solution.Projects)
                {
                    if (project.Kind != Constants.vsProjectKindSolutionItems & project.Kind != Constants.vsProjectKindMisc)
                    {
                        changeCodeAnalysis(project, isCodeAnalysisEnabled);
                    }
                    else
                    {
                        foreach (ProjectItem projectItem in project.ProjectItems)
                        {
                            if (projectItem.SubProject != null)
                            {
                                changeCodeAnalysis(projectItem.SubProject, isCodeAnalysisEnabled);
                            }
                        }
                    }
                }
            }
            catch (Exception exception)
            {
                onWriteToLog(exception.Message);
            }
        }
    }
}